import { Button, instantiate, Node, Prefab } from "cc";
import { App } from "../../../App";
import { EventMgr } from "../../mgr/EventMgr";
import { ResMgr } from "../../mgr/ResMgr";
import { ViewMgr } from "../../mgr/ViewMgr";
import { btnNamePrefix, MyEvent} from "../event/MyEvent";
import { IView, IViewDataProccesor, TViewConfig } from "./IView";
import { ViewComponent } from "./ViewComponent";


export abstract class ViewCtrlBase {

    controller: any = null;
    model: any = null;
    node: Node = null;

    resMgr: ResMgr;
    viewMgr: ViewMgr;
    eventMgr: EventMgr;
    constructor(
        public config: TViewConfig,
        public dep: any[],
    ) {
        this.resMgr = App.ins.resMgr;
        this.viewMgr = App.ins.viewMgr;
        this.eventMgr = App.ins.eventMgr;
    }

    get dataProccesor(): IViewDataProccesor {
        return this.viewMgr.data.dataProccesorMap.get(this.config.id);
    }

    async create(): Promise<Node> {
        return new Promise((resolve:Function,reject:Function)=>{
            const { id, bundle, prefab, atlasArr } = this.config;
            this.resMgr.loadBundle(id, bundle).then(() => {
                this.resMgr.loadAtlasArrByBundle(bundle, atlasArr).then(() => {
                    this.resMgr.loadPrefab(bundle, prefab).then((prefabAsset: Prefab) => {
                        this.parseDep();
                        const node = this.createView(prefabAsset);
                        this.view.viewCtrl = this;
                        this.onShow();
                        resolve(node);
                    })
                })
            })
        })        
    }

    destroy(): void {
        this.onHide();
        this.view.dispose();
        this.dispose();
        this.removeView();
    }

    protected onShow(): void {
        this.showView();
        this.addEvent();
    }

    protected onHide(): void {
        this.hideView();
        this.removeEvent();
    }

    private createView(prefabAsset: Prefab): Node {
        const { name } = this.config;
        const node = instantiate(prefabAsset);
        node.name = name;
        this.node = node;
        return node;
    }

    private removeView(): void {        
        
        if(this.node && this.node.parent){
            this.node.parent.removeFromParent();
        }
        this.node = null;
    }

    private showView(): void {
        this.node.active = true;
    }

    private hideView(): void {
        this.node.active = false;
    }

    close(): void {
        const { id } = this.config;
        this.viewMgr.close(id)
    }

    abstract parseDep(): void;

    abstract get view(): ViewComponent & IView;

    addEvent(): void {
        const btns = this.listClickButtons() || [];
        for (const btn of btns) {
            this.addViewClickEvent(btn);
        }
        const events = this.listViewNodeEvent() || [];
        for (const eName of events) {
            this.addViewNodeEvent(eName);
        }
        const gEvents = this.listGlobalEvent() || [];
        for (const gE of gEvents) {
            this.eventMgr.on(gE, this.onGlobalEvent, this);
        }
    };

    removeEvent(): void {
        const gEvents = this.listGlobalEvent() || [];
        for (const gE of gEvents) {
            this.eventMgr.off(gE, this.onGlobalEvent, this);
        }
    };

    dispose(): void {
        throw new Error('Method not implemented.');
    }

    abstract listClickButtons(): Button[];
    addViewClickEvent(btn: Button): void {
        this.view.addClickEvent(btn, this.onViewClick, this);
    }
    onViewClick(btn: Button): void {
        const name = btn.node.name;
        if (name.indexOf(btnNamePrefix) < 0) {
            console.error("wrong btn name", name);
            return;
        }
        const funcName = name.split("_")[1];
        if (this[funcName]) {
            this[funcName]();
        }
    }

    abstract listViewNodeEvent(): string[];
    addViewNodeEvent(type: string): void {
        this.node.on(type, this.onViewNodeEvent, this)
    }
    onViewNodeEvent(e: MyEvent): void {
        const type = e.type;
        const funcName = type;
        if (this[funcName]) {
            this[funcName](e);
        }
    }

    abstract listGlobalEvent(): string[];
    addGlobalEvent(type: string): void {
        this.eventMgr.on(type, this.onGlobalEvent, this);
    }
    onGlobalEvent(type: string, arg1: any, arg2: any, arg3: any, arg4: any, arg5: any): void {
        const funcName = type;
        if (this[funcName]) {
            this[funcName](arg1, arg2, arg3, arg4, arg5);
        }
    }

}

